package com.ims.redis;

import com.ims.shiro.ShiroSession;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.ValidatingSession;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.*;

/**
 * redis实现共享session
 */
@Component
public class RedisCacheSessionDao extends EnterpriseCacheSessionDAO  {
    private Logger logger = LoggerFactory.getLogger(RedisCacheSessionDao.class);
    private RedisManager redisManager;
    public RedisManager getRedisManager() {
        return redisManager;
    }

    private  long seconds = 0;
    public void setSeconds(long seconds) {
        this.seconds = seconds;
    }
    public void setRedisManager(RedisManager redisManager) {
        this.redisManager = redisManager;
    }

    // 创建session，保存到数据库
    @Override
    protected Serializable doCreate(Session session) {
        Serializable sessionId = super.doCreate(session);
        redisManager.set(sessionId.toString(), sessionToByte((ShiroSession) session),this.seconds);

        return sessionId;
    }

    // 获取session
    @Override
    protected Session doReadSession(Serializable sessionId) {
        // 先从缓存中获取session，如果没有再去数据库中获取
        Session session = super.doReadSession(sessionId);
        if(session == null){
            byte[] bytes = (byte[]) redisManager.get(sessionId.toString());
            if(bytes != null && bytes.length > 0){
                session = byteToSession(bytes);
            }
        }
        System.out.println("读取读取读取读取读取读取读取读取读取读取读取读取读取读取读取读取读取读取读取读取读取读取读取");
        return session;

    }

    // 更新session的最后一次访问时间
    @Override
    protected void doUpdate(Session session) {
        //如果会话过期/停止 没必要再更新了
        try {
             if (session instanceof ValidatingSession && !((ValidatingSession) session).isValid()) {
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            if (session instanceof ShiroSession) {
                // 如果没有主要字段(除lastAccessTime以外其他字段)发生改变
                ShiroSession shiroSession = (ShiroSession) session;
                if (!shiroSession.isChanged()) {
                    return;
                }
                shiroSession.setChanged(false);
                redisManager.set(session.getId().toString(), sessionToByte(shiroSession),this.seconds);
                logger.info("sessionId {} name {} 被更新", session.getId(), session.getClass().getName());

            }
        } catch (Exception e) {
            logger.warn("更新Session失败", e);
        }

    }

    // 删除session
    @Override
    protected void doDelete(Session session) {
        super.doDelete(session);
        this.uncache(session.getId());
        redisManager.remove(session.getId().toString());
    }

    /**
     * 删除cache中缓存的Session
     */
    public void uncache(Serializable sessionId) {
        try {
            Session session = super.getCachedSession(sessionId);
            super.uncache(session);
            System.out.println("shiro session id {} 的缓存失效" + sessionId);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 把session对象转化为byte保存到redis中
    public byte[] sessionToByte(Session session){
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        byte[] bytes = null;
        try {
            ObjectOutput oo = new ObjectOutputStream(bo);
            oo.writeObject(session);
            bytes = bo.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bytes;
    }

    // 把byte还原为session
    public Session byteToSession(byte[] bytes){
        ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
        ObjectInputStream in;
        SimpleSession session = null;
        try {
            in = new ObjectInputStream(bi);
            session = (SimpleSession) in.readObject();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return session;
    }
}
